﻿namespace UnityEngine
{
    using System;
    using System.Collections;

    [AddComponentMenu(""), ExecuteInEditMode]
    public sealed class Terrain : MonoBehaviour
    {
        [NonSerialized]
        private TerrainChangedFlags dirtyFlags;
        [NonSerialized]
        private Terrain m_BottomNeighbor;
        [SerializeField]
        private bool m_CastShadows = true;
        [SerializeField]
        private float m_DetailObjectDensity = 1f;
        [SerializeField]
        private float m_DetailObjectDistance = 80f;
        [SerializeField]
        private bool m_DrawTreesAndFoliage = true;
        [NonSerialized]
        private TerrainRenderFlags m_EditorRenderFlags = TerrainRenderFlags.all;
        [SerializeField]
        private int m_HeightmapMaximumLOD;
        [SerializeField]
        private float m_HeightmapPixelError = 5f;
        [NonSerialized]
        private Terrain m_LeftNeighbor;
        [SerializeField]
        private int m_LightmapIndex = -1;
        [SerializeField]
        private int m_LightmapSize = 0x400;
        [SerializeField]
        private Material m_MaterialTemplate;
        [NonSerialized]
        private Vector3 m_Position;
        [NonSerialized]
        private Terrain m_RightNeighbor;
        [SerializeField]
        private float m_SplatMapDistance = 1000f;
        [SerializeField]
        private TerrainData m_TerrainData;
        [NonSerialized]
        private Terrain m_TopNeighbor;
        [SerializeField]
        private float m_TreeBillboardDistance = 50f;
        [SerializeField]
        private float m_TreeCrossFadeLength = 5f;
        [SerializeField]
        private float m_TreeDistance = 5000f;
        [SerializeField]
        private int m_TreeMaximumFullLODCount = 50;
        private static Terrain ms_ActiveTerrain;
        internal static ArrayList ms_ActiveTerrains = new ArrayList();
        private static ArrayList ms_TempCulledTerrains = new ArrayList();
        [NonSerialized]
        private ArrayList renderers = new ArrayList();

        public void AddTreeInstance(TreeInstance instance)
        {
            bool flag = this.m_TerrainData.HasTreeInstances();
            this.m_TerrainData.AddTree(out instance);
            IEnumerator enumerator = this.renderers.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Renderer current = (Renderer) enumerator.Current;
                    if (flag)
                    {
                        current.trees.InjectTree(out instance);
                    }
                    else
                    {
                        current.trees.Cleanup();
                        current.trees = new TreeRenderer(this.m_TerrainData, this.GetPosition(), this.m_LightmapIndex);
                    }
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        }

        internal void ApplyDelayedHeightmapModification()
        {
            if (this.terrainData.ComputeDelayedLod().Length != 0)
            {
                this.terrainData.RecalculateTreePositions();
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer current = (Renderer) enumerator.Current;
                        current.terrain.ReloadPrecomputedError();
                        current.terrain.ReloadBounds();
                        current.details.ReloadAllDetails();
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
        }

        public static GameObject CreateTerrainGameObject(TerrainData assignTerrain)
        {
            System.Type[] components = new System.Type[] { typeof(Terrain), typeof(TerrainCollider) };
            GameObject obj2 = new GameObject("Terrain", components) {
                isStatic = true
            };
            Terrain component = obj2.GetComponent(typeof(Terrain)) as Terrain;
            TerrainCollider collider = obj2.GetComponent(typeof(TerrainCollider)) as TerrainCollider;
            collider.terrainData = assignTerrain;
            component.terrainData = assignTerrain;
            component.OnEnable();
            return obj2;
        }

        private static void CullAllTerrains(int cullingMask)
        {
            ms_TempCulledTerrains.Clear();
            int count = ms_ActiveTerrains.Count;
            for (int i = 0; i < count; i++)
            {
                Terrain terrain = (Terrain) ms_ActiveTerrains[i];
                int layer = terrain.gameObject.layer;
                if (((((int) 1) << layer) & cullingMask) != 0)
                {
                    ms_TempCulledTerrains.Add(terrain);
                    Vector3 position = terrain.GetPosition();
                    if (position != terrain.m_Position)
                    {
                        terrain.m_Position = position;
                        terrain.Flush();
                    }
                    terrain.GarbageCollectRenderers();
                    terrain.FlushDirty();
                    Renderer renderer = terrain.GetRenderer();
                    if (renderer != null)
                    {
                        terrain.terrainData.RecalculateBasemapIfDirty();
                        if ((terrain.m_EditorRenderFlags & TerrainRenderFlags.heightmap) != ((TerrainRenderFlags) 0))
                        {
                            float splatDistance = (terrain.m_EditorRenderFlags != TerrainRenderFlags.heightmap) ? terrain.m_SplatMapDistance : float.PositiveInfinity;
                            renderer.terrain.RenderStep1(renderer.camera, terrain.m_HeightmapMaximumLOD, terrain.m_HeightmapPixelError, splatDistance);
                        }
                    }
                }
            }
            count = ms_TempCulledTerrains.Count;
            for (int j = 0; j < count; j++)
            {
                Terrain terrain2 = (Terrain) ms_TempCulledTerrains[j];
                TerrainRenderer terrainRendererDontCreate = terrain2.GetTerrainRendererDontCreate();
                if ((terrainRendererDontCreate != null) && ((terrain2.m_EditorRenderFlags & TerrainRenderFlags.heightmap) != ((TerrainRenderFlags) 0)))
                {
                    TerrainRenderer left = null;
                    TerrainRenderer right = null;
                    TerrainRenderer top = null;
                    TerrainRenderer bottom = null;
                    if (terrain2.m_LeftNeighbor != null)
                    {
                        left = terrain2.m_LeftNeighbor.GetTerrainRendererDontCreate();
                    }
                    if (terrain2.m_RightNeighbor != null)
                    {
                        right = terrain2.m_RightNeighbor.GetTerrainRendererDontCreate();
                    }
                    if (terrain2.m_TopNeighbor != null)
                    {
                        top = terrain2.m_TopNeighbor.GetTerrainRendererDontCreate();
                    }
                    if (terrain2.m_BottomNeighbor != null)
                    {
                        bottom = terrain2.m_BottomNeighbor.GetTerrainRendererDontCreate();
                    }
                    terrainRendererDontCreate.SetNeighbors(left, top, right, bottom);
                }
            }
            count = ms_TempCulledTerrains.Count;
            for (int k = 0; k < count; k++)
            {
                Terrain terrain3 = (Terrain) ms_TempCulledTerrains[k];
                TerrainRenderer renderer7 = terrain3.GetTerrainRendererDontCreate();
                if ((renderer7 != null) && ((terrain3.m_EditorRenderFlags & TerrainRenderFlags.heightmap) != ((TerrainRenderFlags) 0)))
                {
                    renderer7.RenderStep2();
                }
            }
            count = ms_TempCulledTerrains.Count;
            for (int m = 0; m < count; m++)
            {
                Terrain terrain4 = (Terrain) ms_TempCulledTerrains[m];
                Renderer renderer8 = terrain4.GetRenderer();
                if (renderer8 != null)
                {
                    int num8 = terrain4.gameObject.layer;
                    Light[] lights = Light.GetLights(LightType.Directional, num8);
                    if ((terrain4.m_EditorRenderFlags & TerrainRenderFlags.heightmap) != ((TerrainRenderFlags) 0))
                    {
                        renderer8.terrain.RenderStep3(renderer8.camera, num8, terrain4.m_CastShadows, terrain4.materialTemplate);
                    }
                    if ((((terrain4.m_EditorRenderFlags & TerrainRenderFlags.details) != ((TerrainRenderFlags) 0)) && terrain4.m_DrawTreesAndFoliage) && (terrain4.m_DetailObjectDistance > 0.001))
                    {
                        renderer8.details.Render(renderer8.camera, terrain4.m_DetailObjectDistance, num8, terrain4.m_DetailObjectDensity);
                    }
                    if ((((terrain4.m_EditorRenderFlags & TerrainRenderFlags.trees) != ((TerrainRenderFlags) 0)) && terrain4.m_DrawTreesAndFoliage) && (terrain4.m_TreeDistance > 0.001))
                    {
                        renderer8.trees.Render(renderer8.camera, lights, terrain4.m_TreeBillboardDistance, terrain4.m_TreeDistance, terrain4.m_TreeCrossFadeLength, terrain4.m_TreeMaximumFullLODCount, num8);
                    }
                }
            }
        }

        private static void CullAllTerrainsShadowCaster(Light light)
        {
            IEnumerator enumerator = ms_ActiveTerrains.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Terrain current = (Terrain) enumerator.Current;
                    Renderer renderer = current.GetRenderer();
                    if (((renderer != null) && ((current.m_EditorRenderFlags & TerrainRenderFlags.trees) != ((TerrainRenderFlags) 0))) && (current.m_DrawTreesAndFoliage && (current.m_TreeDistance > 0.001)))
                    {
                        renderer.trees.RenderShadowCasters(light, renderer.camera, Mathf.Min(current.m_TreeBillboardDistance, current.m_TreeDistance), current.m_TreeMaximumFullLODCount, current.gameObject.layer);
                    }
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        }

        public void Flush()
        {
            IEnumerator enumerator = this.renderers.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Renderer current = (Renderer) enumerator.Current;
                    current.trees.Cleanup();
                    current.terrain.Dispose();
                    current.details.Dispose();
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
            this.renderers = new ArrayList();
        }

        private void FlushDirty()
        {
            bool flag = false;
            bool flag2 = false;
            if ((this.dirtyFlags & TerrainChangedFlags.Heightmap) != 0)
            {
                flag = flag2 = true;
            }
            if ((this.dirtyFlags & TerrainChangedFlags.TreeInstances) != 0)
            {
                flag2 = true;
            }
            if ((this.dirtyFlags & TerrainChangedFlags.DelayedHeightmapUpdate) != 0)
            {
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer current = (Renderer) enumerator.Current;
                        current.terrain.ReloadPrecomputedError();
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
            if (flag2)
            {
                IEnumerator enumerator2 = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator2.MoveNext())
                    {
                        Renderer renderer2 = (Renderer) enumerator2.Current;
                        renderer2.trees.ReloadTrees();
                    }
                }
                finally
                {
                    IDisposable disposable2 = enumerator2 as IDisposable;
                    if (disposable2 == null)
                    {
                    }
                    disposable2.Dispose();
                }
            }
            if (flag)
            {
                IEnumerator enumerator3 = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator3.MoveNext())
                    {
                        Renderer renderer3 = (Renderer) enumerator3.Current;
                        renderer3.details.ReloadAllDetails();
                    }
                }
                finally
                {
                    IDisposable disposable3 = enumerator3 as IDisposable;
                    if (disposable3 == null)
                    {
                    }
                    disposable3.Dispose();
                }
            }
            if ((this.dirtyFlags & TerrainChangedFlags.Heightmap) != 0)
            {
                IEnumerator enumerator4 = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator4.MoveNext())
                    {
                        Renderer renderer4 = (Renderer) enumerator4.Current;
                        renderer4.terrain.ReloadAll();
                    }
                }
                finally
                {
                    IDisposable disposable4 = enumerator4 as IDisposable;
                    if (disposable4 == null)
                    {
                    }
                    disposable4.Dispose();
                }
            }
            this.dirtyFlags = 0;
        }

        private void GarbageCollectRenderers()
        {
            int renderedFrameCount = Time.renderedFrameCount;
            for (int i = this.renderers.Count - 1; i >= 0; i--)
            {
                Renderer renderer = (Renderer) this.renderers[i];
                int num3 = renderedFrameCount - renderer.lastUsedFrame;
                if (((num3 > 100) || (num3 < 0)) || (renderer.camera == null))
                {
                    renderer.trees.Cleanup();
                    renderer.terrain.Dispose();
                    renderer.details.Dispose();
                    this.renderers.RemoveAt(i);
                }
            }
        }

        public Vector3 GetPosition()
        {
            return base.transform.position;
        }

        private Renderer GetRenderer()
        {
            Camera current = Camera.current;
            if ((current.cullingMask & (((int) 1) << base.gameObject.layer)) == 0)
            {
                return null;
            }
            if (!current.IsFiltered(base.gameObject))
            {
                return null;
            }
            int renderedFrameCount = Time.renderedFrameCount;
            IEnumerator enumerator = this.renderers.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Renderer renderer = (Renderer) enumerator.Current;
                    if (renderer.camera == current)
                    {
                        if (renderer.terrain.terrainData == null)
                        {
                            this.Flush();
                            goto Label_00C0;
                        }
                        renderer.lastUsedFrame = renderedFrameCount;
                        return renderer;
                    }
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        Label_00C0:
            if (this.m_TerrainData != null)
            {
                Vector3 position = this.GetPosition();
                Renderer renderer2 = new Renderer {
                    camera = current,
                    terrain = new TerrainRenderer(base.gameObject.GetInstanceID(), this.m_TerrainData, position, this.lightmapIndex)
                };
                renderer2.terrain.SetLightmapSize(this.m_LightmapSize);
                renderer2.trees = new TreeRenderer(this.m_TerrainData, position, this.lightmapIndex);
                renderer2.details = new DetailRenderer(this.m_TerrainData, position, this.lightmapIndex);
                renderer2.lastUsedFrame = renderedFrameCount;
                this.renderers.Add(renderer2);
                return renderer2;
            }
            return null;
        }

        private TerrainRenderer GetTerrainRendererDontCreate()
        {
            Camera current = Camera.current;
            if ((current.cullingMask & (((int) 1) << base.gameObject.layer)) != 0)
            {
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer renderer = (Renderer) enumerator.Current;
                        if (renderer.camera == current)
                        {
                            return renderer.terrain;
                        }
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
            return null;
        }

        private void OnDisable()
        {
            ms_ActiveTerrains.Remove(this);
            if (ms_ActiveTerrain == this)
            {
                ms_ActiveTerrain = null;
            }
            if (this.m_TerrainData != null)
            {
                this.m_TerrainData.RemoveUser(base.gameObject);
            }
            ms_ActiveTerrains.Remove(this);
            this.Flush();
        }

        private void OnEnable()
        {
            if (this.m_TerrainData != null)
            {
                this.m_TerrainData.AddUser(base.gameObject);
            }
            ms_ActiveTerrain = this;
            if (!ms_ActiveTerrains.Contains(this))
            {
                ms_ActiveTerrains.Add(this);
            }
        }

        private void OnTerrainChanged(TerrainChangedFlags flags)
        {
            if ((flags & TerrainChangedFlags.RemoveDirtyDetailsImmediately) != 0)
            {
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer current = (Renderer) enumerator.Current;
                        current.details.ReloadDirtyDetails();
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
            if ((flags & TerrainChangedFlags.FlushEverythingImmediately) != 0)
            {
                this.Flush();
            }
            else
            {
                this.dirtyFlags |= flags;
            }
        }

        private static void ReconnectTerrainData()
        {
            IEnumerator enumerator = new ArrayList(ms_ActiveTerrains).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Terrain current = (Terrain) enumerator.Current;
                    if (current.m_TerrainData == null)
                    {
                        current.OnDisable();
                    }
                    else if (!current.m_TerrainData.HasUser(current.gameObject))
                    {
                        current.OnDisable();
                        current.OnEnable();
                    }
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        }

        internal void RemoveTrees(Vector2 position, float radius, int prototypeIndex)
        {
            if (this.m_TerrainData.RemoveTrees(position, radius, prototypeIndex) != 0)
            {
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer current = (Renderer) enumerator.Current;
                        current.trees.RemoveTrees((Vector3) position, radius, prototypeIndex);
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
        }

        public float SampleHeight(Vector3 worldPosition)
        {
            worldPosition -= this.GetPosition();
            worldPosition.x /= this.m_TerrainData.size.x;
            worldPosition.z /= this.m_TerrainData.size.z;
            return this.m_TerrainData.GetInterpolatedHeight(worldPosition.x, worldPosition.z);
        }

        private void SetLightmapIndex(int value)
        {
            this.m_LightmapIndex = value;
            IEnumerator enumerator = this.renderers.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    Renderer current = (Renderer) enumerator.Current;
                    current.terrain.lightmapIndex = value;
                    current.trees.lightmapIndex = value;
                    current.details.lightmapIndex = value;
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        }

        private static void SetLightmapIndexOnAllTerrains(int lightmapIndex)
        {
            IEnumerator enumerator = ms_ActiveTerrains.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    ((Terrain) enumerator.Current).SetLightmapIndex(lightmapIndex);
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable == null)
                {
                }
                disposable.Dispose();
            }
        }

        public void SetNeighbors(Terrain left, Terrain top, Terrain right, Terrain bottom)
        {
            this.m_TopNeighbor = top;
            this.m_LeftNeighbor = left;
            this.m_RightNeighbor = right;
            this.m_BottomNeighbor = bottom;
        }

        private void ShiftLightmapIndex(int offset)
        {
            this.m_LightmapIndex += offset;
        }

        public static Terrain activeTerrain
        {
            get
            {
                return ms_ActiveTerrain;
            }
        }

        public float basemapDistance
        {
            get
            {
                return this.m_SplatMapDistance;
            }
            set
            {
                this.m_SplatMapDistance = value;
            }
        }

        public bool castShadows
        {
            get
            {
                return this.m_CastShadows;
            }
            set
            {
                this.m_CastShadows = value;
            }
        }

        public float detailObjectDensity
        {
            get
            {
                return this.m_DetailObjectDensity;
            }
            set
            {
                value = Mathf.Clamp(value, 0f, 1f);
                bool flag = !(value == this.m_DetailObjectDensity);
                this.m_DetailObjectDensity = value;
                if (flag)
                {
                    IEnumerator enumerator = this.renderers.GetEnumerator();
                    try
                    {
                        while (enumerator.MoveNext())
                        {
                            Renderer current = (Renderer) enumerator.Current;
                            current.details.ReloadAllDetails();
                        }
                    }
                    finally
                    {
                        IDisposable disposable = enumerator as IDisposable;
                        if (disposable == null)
                        {
                        }
                        disposable.Dispose();
                    }
                }
            }
        }

        public float detailObjectDistance
        {
            get
            {
                return this.m_DetailObjectDistance;
            }
            set
            {
                this.m_DetailObjectDistance = value;
            }
        }

        internal bool drawTreesAndFoliage
        {
            get
            {
                return this.m_DrawTreesAndFoliage;
            }
            set
            {
                this.m_DrawTreesAndFoliage = value;
            }
        }

        public TerrainRenderFlags editorRenderFlags
        {
            get
            {
                return this.m_EditorRenderFlags;
            }
            set
            {
                this.m_EditorRenderFlags = value;
            }
        }

        public int heightmapMaximumLOD
        {
            get
            {
                return this.m_HeightmapMaximumLOD;
            }
            set
            {
                this.m_HeightmapMaximumLOD = value;
            }
        }

        public float heightmapPixelError
        {
            get
            {
                return this.m_HeightmapPixelError;
            }
            set
            {
                this.m_HeightmapPixelError = value;
            }
        }

        public int lightmapIndex
        {
            get
            {
                return this.m_LightmapIndex;
            }
            set
            {
                this.SetLightmapIndex(value);
            }
        }

        internal int lightmapSize
        {
            get
            {
                return this.m_LightmapSize;
            }
            set
            {
                this.m_LightmapSize = (value <= 0) ? 1 : value;
                IEnumerator enumerator = this.renderers.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Renderer current = (Renderer) enumerator.Current;
                        current.terrain.SetLightmapSize(value);
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator as IDisposable;
                    if (disposable == null)
                    {
                    }
                    disposable.Dispose();
                }
            }
        }

        public Material materialTemplate
        {
            get
            {
                return this.m_MaterialTemplate;
            }
            set
            {
                this.m_MaterialTemplate = value;
            }
        }

        [Obsolete("use basemapDistance", true)]
        public float splatmapDistance
        {
            get
            {
                return this.m_SplatMapDistance;
            }
            set
            {
                this.m_SplatMapDistance = value;
            }
        }

        public TerrainData terrainData
        {
            get
            {
                return this.m_TerrainData;
            }
            set
            {
                this.m_TerrainData = value;
            }
        }

        public float treeBillboardDistance
        {
            get
            {
                return this.m_TreeBillboardDistance;
            }
            set
            {
                this.m_TreeBillboardDistance = value;
            }
        }

        public float treeCrossFadeLength
        {
            get
            {
                return this.m_TreeCrossFadeLength;
            }
            set
            {
                this.m_TreeCrossFadeLength = value;
            }
        }

        public float treeDistance
        {
            get
            {
                return this.m_TreeDistance;
            }
            set
            {
                this.m_TreeDistance = value;
            }
        }

        public int treeMaximumFullLODCount
        {
            get
            {
                return this.m_TreeMaximumFullLODCount;
            }
            set
            {
                this.m_TreeMaximumFullLODCount = value;
            }
        }

        internal sealed class Renderer
        {
            internal Camera camera;
            internal DetailRenderer details;
            internal int lastUsedFrame;
            internal TerrainRenderer terrain;
            internal TreeRenderer trees;
        }
    }
}

